home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / pine / ccmd / cmnum.c < prev    next >
Encoding:
C/C++ Source or Header  |  1988-08-19  |  9.5 KB  |  279 lines

  1. /*
  2.  Author: Andrew Lowry
  3.  
  4.  Columbia University Center for Computing Activities, July 1986.
  5.  Copyright (C) 1986, 1987, Trustees of Columbia University in the City
  6.  of New York.  Permission is granted to any individual or institution
  7.  to use, copy, or redistribute this software so long as it is not sold
  8.  for profit, provided this copyright notice is retained.
  9. */
  10. /* cmnum
  11. **
  12. ** This module contains the standard break table and handlers for
  13. ** parsing integers.  Parsing succeeds if current input contains an
  14. ** integer in a given radix (up to 16), and the return value is the int
  15. ** value parsed.  Optional flags can specify that a sign may or may not
  16. ** prefix the integer, and whether a successful parse should result if
  17. ** the integer is terminated by anything other than punctuation (as in
  18. ** "12am" as opposed to "12 am").  If parse-only is specified in the
  19. ** FDB (flag NUM_PO), the syntax of the number is checked, but no attempt
  20. ** at binary conversion is made, so overflow errors cannot occur.  In this
  21. ** case, the return value is the (possibly signed) string representing
  22. ** the integer.  It is left in the atom buffer.
  23. **
  24. ** Full completion simply terminates current input with a space if
  25. ** there is a valid integer.  If the input is empty or consists only
  26. ** of a sign, full completion beeps.  Partial completion always beeps.
  27. ** The standard break table allows digits, and letters A-F (and a-f).
  28. ** Minus and plus signs are also allowed in first position.
  29. **/
  30.  
  31. #define    NUMERR            /* declare cmnum parse errors here */
  32.  
  33. #include "ccmdlib.h"        /* get ccmd package symbols */
  34. #include "cmfncs.h"        /* and internal symbols */
  35.  
  36. /* Forward declaration of handler routines */
  37.  
  38. int numprs(), numhlp(), numcplt();
  39.  
  40. static brktab numbrk = {
  41.   {                    /* 1st char break array */
  42.     0xff, 0xff, 0xff, 0xff, 0xff, 0xeb, 0x00, 0x3f, /* allow - and + */
  43.     0x81, 0xff, 0xff, 0xff, 0x81, 0xff, 0xff, 0xff
  44.   },
  45.   {                    /* subsequent char break array */
  46.     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x3f, /* disallow - and + */
  47.     0x81, 0xff, 0xff, 0xff, 0x81, 0xff, 0xff, 0xff
  48.   }
  49. };
  50.  
  51. ftspec ft_num = { numprs, numhlp, numcplt, 0, &numbrk }; /* handler structure */
  52.  
  53.  
  54.  
  55. /* numprs - Locate the digit string in the current input.  If there is
  56. ** none, error.  If NUM_PO is on in the FDB, the integer is parsed and
  57. ** returned as a string, with no attempt at binary conversion.  Otherwise
  58. ** convert to binary, keeping watch for overflow.
  59. **/
  60.  
  61. PASSEDSTATIC int
  62. numprs(text,textlen,fdbp,parselen,value)
  63. char *text;
  64. int textlen,*parselen;
  65. fdb *fdbp;
  66. pval *value;
  67. {
  68.   char *digits,*term,c;        /* for decomposing the input */
  69.   int sign;
  70.   int ndig;            /* number of digits */
  71.   int radix;            /* radix specified in the FDB */
  72.   int flags;            /* flags specified in the FDB */
  73.   unsigned int valhi,vallo;    /* high and low pieces of binary value */
  74.   int dig;            /* integer value of individual digit */
  75.   int intbits;            /* number of bits per integer */
  76.   int ret;            /* return code from aux routine */
  77.  
  78.   radix = (int) fdbp->_cmdat;    /* get the radix */
  79.   flags = fdbp->_cmffl;        /* and parse flags */
  80.                 /* parse the input */
  81.   ret = prsnum(text,textlen,flags,radix,&sign,&digits,&ndig,&term);
  82.   if (ret != CMxOK)
  83.     return(ret);        /* propagate problems */
  84.   if (term == NULL)
  85.     return(CMxINC);        /* unterminated field is incomplete */
  86.   if (digits == NULL)
  87.     return(NUMxNP);        /* terminated but no digits */
  88.   if (ndig < 1)            /* check ndig too - chris */
  89.     return (NUMxNP);        /* terminated but no digits */
  90.   if (flags & NUM_PO) {        /* parse only? */
  91.     value->_pvstr = cmcsb._cmabp; /* yup, string value in atom buffer */
  92.     *parselen = term - text;    /* compute number of characters */
  93.     return(CMxOK);
  94.   }
  95.   valhi =  -1;            /* count bits */
  96.   for (intbits = 0; valhi != 0; intbits++)
  97.     valhi <<= 1;        /* shift out bits until zeros remain */
  98.  
  99.   valhi = vallo = 0;        /* start with zero, then add in digits */
  100.   while (ndig-- > 0) {        /* loop through all the digits */
  101.     vallo *= radix;        /* multiply both pieces by radix */
  102.     valhi *= radix;
  103.     dig = ((*digits++) & CC_CHR) - '0'; /* get value of next digit */
  104.     if (dig > 9)
  105.       dig -= ('A'-'0')-10;    /* adjust non-numeric digits */
  106.     if (dig > 15)
  107.       dig -= 'a'-'A';        /* further adjust lowercase */
  108.     vallo += dig;        /* add in the new digit */
  109.     valhi += vallo >> 4;    /* leave only 4 bits in low part */
  110.     vallo &= 0xf;
  111.     if ((valhi >> (intbits-4)) != 0)
  112.       return(NUMxOV);        /* overflow */
  113.   }                /* go do next digit */
  114.   if (flags & NUM_US)        /* unsigned integer? */
  115.     valhi = (valhi << 4) | vallo; /* shift all bits into high piece */
  116.   else if (sign == -1)        /* need to negate it? */
  117.     if ((valhi >> intbits-5) == 0) /* in range for positive? */
  118.       valhi = -((valhi << 4) | vallo); /* yup, make abs val and negate */
  119.     else if (((valhi << 5) == 0) && (vallo == 0))
  120.       valhi = 1 << (intbits-1);    /* no, handle special case -2^(intbits-1) */
  121.     else
  122.       return(NUMxOV);        /* overflow */
  123.   else                /* positive signed integer */
  124.     if ((valhi >> intbits-5) == 0) /* in range? */
  125.       valhi = (valhi << 4) | vallo; /* yup, put pieces together */
  126.     else
  127.       return(NUMxOV);        /* nope, overflow */
  128.  
  129.   value->_pvint = (int) valhi;    /* set value in place */
  130.   *parselen = term - text;    /* compute consumed region */
  131.   return(CMxOK);        /* and give a good parse */
  132. }
  133.  
  134.  
  135.  
  136. /* numcplt - Partial completion always comes up empty.  Full completion
  137. ** gets a space if there are any digits in the buffer.
  138. **/
  139.  
  140. PASSEDSTATIC int
  141. numcplt(text,textlen,fdbp,full,cplt,cpltlen)
  142. char *text,**cplt;
  143. int textlen,full,*cpltlen;
  144. fdb *fdbp;
  145. {
  146.   int radix,flags;        /* radix and flags for parse */
  147.   char *digits,*term;        /* decomposed input */
  148.   int sign,ndig;
  149.   int ret;            /* return code from aux routine */
  150.  
  151.   radix = (int) fdbp->_cmdat;    /* get radix and flags */
  152.   flags = fdbp->_cmffl;
  153.                 /* try to parse the data */
  154.   ret = prsnum(text,textlen,flags,radix,&sign,&digits,&ndig,&term);
  155.  
  156.   *cplt = NULL;            /* we never supply completion text */
  157.   if (!full || (ndig == 0))    /* partial or no digits yet? */
  158.     return(CMP_BEL);        /* then just beep */
  159.   else
  160.     return(CMP_SPC | CMP_GO);    /* full gets a space and wakeup */
  161. }
  162.  
  163.  
  164.  
  165. /* numhlp - Just give a message customized by the requested radix.
  166. **/
  167.  
  168. PASSEDSTATIC int
  169. numhlp(text,textlen,fdbp,cust)
  170. char *text;
  171. int textlen,cust;
  172. fdb *fdbp;
  173. {
  174.   int radix;            /* requested radix and flags */
  175.   int flags;
  176.   int caseadj = 0;        /* used when lowercasing initials */
  177.   char raddigs[3];        /* space for decimal string for radix value */
  178.  
  179. /* macro to print upper or lowercase char depending on value of caseadj */
  180. #define caseout(x) cmxputc((char) (x)+caseadj)
  181.  
  182.   radix = (int) fdbp->_cmdat;    /* get flags and radix */
  183.   flags = fdbp->_cmffl;
  184.   
  185.   if (flags & NUM_US) {
  186.     cmxputs("Unsigned ");    /* indicate if signs not allowed */
  187.     caseadj = 'a'-'A';        /* set up for lowercase initials */
  188.   }
  189.   switch (radix) {
  190.     case 2: caseout('B'); cmxputs("inary integer"); break;
  191.     case 8: caseout('O'); cmxputs("ctal integer"); break;
  192.     case 10: caseout('D'); cmxputs("ecimal integer"); break;
  193.     case 16: caseout('H'); cmxputs("exadecimal integer"); break;
  194.     default: caseout('I'); cmxputs("nteger in base ");
  195.       if (radix >= 10)        /* output radix in decimal */
  196.     cmxputc('1');
  197.       cmxputc((char) (radix % 10) + '0');
  198.       break;
  199.   }
  200.   return(CMxOK);
  201. }
  202.  
  203.  
  204.  
  205. /* prsnum - Auxiliary routine for parse and completion.
  206. ** 
  207. ** Purpose:
  208. **   Breaks the current input into three pieces - sign, digits, and
  209. **   terminator, and checks to make sure that restrictions called
  210. **   for in the parse data flags are observed.
  211. **
  212. ** Input arguments:
  213. **   text - Pointer to input text.
  214. **   textlen - Number of characters of input text.
  215. **   flags - Flags supplied in the FDB.
  216. **   radix - Radix specified in the FDB.
  217. **
  218. ** Output arguments:
  219. **   sign - 0 for no sign, 1 for +, -1 for -.
  220. **   digits - Points to beginning of digit string.
  221. **   ndig - Number of digits in digit string.
  222. **   term - Points to terminating character.
  223. **
  224. ** Returns: Standard error code.
  225. **/
  226.  
  227. static int
  228. prsnum(text,textlen,flags,radix,sign,digits,ndig,term)
  229. char *text, **digits, **term;
  230. int textlen, flags, radix, *sign, *ndig;
  231. {
  232.   int dig;            /* value of individual digit */
  233.   char c;
  234.  
  235.   if ((radix < 2) || (radix > 16)) /* check for bad radix */
  236.     return(NUMxRAD);
  237.  
  238.   *ndig = 0;            /* no digits scanned yet */
  239.   *sign = 0;            /* assume no sign */
  240.   *digits = text;        /* so digits start right away */
  241.   *term = NULL;            /* and no terminator given */
  242.  
  243.   if (textlen == 0)
  244.     return(CMxOK);        /* no text - all assumptions true */
  245.  
  246.   c = *text & CC_CHR;        /* pick up first char */
  247.   if ((c == '+') || (c == '-')) { /* sign at front? */
  248.     if (flags & NUM_US)        /* yup, check unsigned flag */
  249.       return(NUMxSGN);        /* was not supposed to be there */
  250.     *sign = (c == '+') ? 1 : -1; /* set the sign value */
  251.     *digits = ++text;        /* move digit start */
  252.     textlen--;            /* and count the character*/
  253.   }
  254.  
  255.   while (textlen-- > 0) {
  256.     c = *text & CC_CHR;        /* get next digit */
  257.     if (isdigit(c))
  258.       dig = c - '0';        /* adjust numeric digits */
  259.     else if (isupper(c))
  260.       dig = (c - 'A') + 10;    /* adjust uppercase digits */
  261.     else if (islower(c))
  262.       dig = (c - 'a') + 10;    /* adjust lowercase digits */
  263.     else
  264.       dig = 16;            /* anything else invalid */
  265.     if (dig >= radix)        /* terminator found? */
  266.       if (!isalnum(c) || (flags & NUM_BNP)) { /* valid terminator? */
  267.     *term = text;        /* point at terminator */
  268.         return(CMxOK);        /* and return */
  269.       }
  270.       else
  271.     return(NUMxNP);        /* bad terminator */
  272.     else {
  273.       (*ndig)++;        /* else bump digit counter */
  274.       text++;            /* and move on */
  275.     }
  276.   }
  277.   return(CMxOK);
  278. }
  279.